﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Quail.Helpers
{
    /// <summary>
    /// Extension methods for the IEnumerable class
    /// </summary>
    public static class Enumerable 
    {
        /// <summary>
        /// Returns a random item (or null) from the enumeration
        /// </summary>
        /// <typeparam name="T">the type of the item</typeparam>
        /// <param name="items">the enumeration</param>
        /// <returns>a random item (or null)</returns>
        public static T RandomItem<T>(this IList<T> items)
        {
            var count = items.Count;

            if (count == 0)
                return default(T);

            var idx = RandomStuff.RandomNumber(0, count - 1);
            return items[idx];
        }

        /// <summary>
        /// Returns a random character from the string
        /// </summary>
        /// <param name="str">the string</param>
        /// <returns>a random character</returns>
        public static char GetRandomCharacter(this string str)
        {
            var count = str.Length;

            if (count == 0)
                throw new ArgumentException("str must contain at least one character", str);

            var idx = RandomStuff.RandomNumber(0, count - 1);
            return str[idx];
        }

        /// <summary>
        /// Returns a random item (or null) from the enumeration
        /// </summary>
        /// <typeparam name="T">the type of the item</typeparam>
        /// <param name="items">the enumeration</param>
        /// <returns>a random item (or null)</returns>
        public static T RandomItem<T>(this IEnumerable<T> items)
        {
            return items.Select(x => new { key = Guid.NewGuid(), item = x })
                .OrderBy(x => x.key)
                .Select(x => x.item)
                .FirstOrDefault();
        }

        /// <summary>
        /// Returns an enumeration of up to count random items
        /// </summary>
        /// <typeparam name="T">the type of the item</typeparam>
        /// <param name="items">the enumeration</param>
        /// <param name="count">how many random items to return</param>
        /// <returns>a new enumeration of up to count items</returns>
        public static IEnumerable<T> RandomItems<T>(this IEnumerable<T> items, int count)
        {
            return items.Select(x => new { key = Guid.NewGuid(), item = x })
                .OrderBy(x => x.key)
                .Select(x => x.item)
                .Take(count);
        }

        /// <summary>
        /// Just randomizes the collection you get back
        /// </summary>
        /// <typeparam name="T">the type of the item</typeparam>
        /// <param name="items">the enumeration</param>
        /// <returns>the same enumerable object</returns>
        public static IEnumerable<T> RandomItems<T>(this IEnumerable<T> items)
        {
            return items.OrderBy(t => Guid.NewGuid());
        }

        /// <summary>
        /// Executes the action on all items in the enumeration
        /// </summary>
        /// <typeparam name="T">the type of the item</typeparam>
        /// <param name="items">the enumeration</param>
        /// <param name="action">the action to perform</param>
        public static void OnEachItem<T>(this IEnumerable<T> items, Action<T> action)
        {
            items.ForEachItem(action);
        }

        /// <summary>
        /// Concatenats an enumeration of strings using a specific separator (similar to String.Join)
        /// </summary>
        /// <param name="strarr"></param>
        /// <param name="separator"></param>
        /// <returns>A single string</returns>
        public static string JoinStrings(this IEnumerable<string> strarr, string separator)
        {
            var result = string.Empty;

            foreach (var str in strarr)
            {
                if (result.Length > 0 && separator != null)
                    result += separator;
                result += str;
            }

            return result;
        }

        /// <summary>
        /// Returns a distinct list of an item based on a record that returns the key value
        /// </summary>
        /// <typeparam name="T">The type of item</typeparam>
        /// <typeparam name="T2">The type of key</typeparam>
        /// <param name="list">The list to enumerate</param>
        /// <param name="getkey">The delegate returning the key</param>
        /// <returns></returns>
        public static IEnumerable<T> Distinct<T, T2>(this IEnumerable<T> list, Func<T, T2> getkey)
        {
            var mydict = new Dictionary<T2, T>();
            list.ForEachItem(l =>
                             {
                                 var k = getkey(l);
                                 if (!mydict.ContainsKey(k))
                                     mydict.Add(k, l);
                             });
            return mydict.Values;
        }

        /// <summary>
        /// Implementation of ForEach() on an IEnumerable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="e"></param>
        /// <param name="a"></param>
        /// <returns></returns>
        public static void ForEachItem<T>(this IEnumerable<T> e, Action<T> a)
        {
            foreach (var i in e)
                a(i);
        }

        /// <summary>
        /// Implementation of ForEach() on an IEnumerable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="e"></param>
        /// <param name="a"></param>
        /// <returns></returns>
        public static void ForEachItem<T>(this IEnumerable<T> e, Action<T, int> a)
        {
            var idx = 0;
            foreach(var i in e)
            {
                a(i, idx);
                idx++;
            }
        }
    }
}
